package com.speechb.modules.shiro;

import com.alibaba.fastjson.JSON;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Administrator on 2017/12/11.
 * 自定义sessionId获取
 */
public class MySessionManager extends DefaultWebSessionManager {
    private static Logger logger = LoggerFactory.getLogger(DefaultWebSessionManager.class);
    private static final String AUTHORIZATION = "Authorization";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    private static final Map<Serializable, Session> sessionMap = new ConcurrentHashMap<>();
    private static RedisTemplate<String, Session> REDIS_TEMPLATE = null;
    public MySessionManager(RedisTemplate redisTemplate) {
        super();
        REDIS_TEMPLATE = redisTemplate;
    }

    @Override
    protected void onStart(Session session, SessionContext context) {
        logger.debug("[session啊]创建session啊" + session.getId());
        putSession(session);
        logger.debug("[session啊]" +sessionMap.size()+":数据量");
        super.onStart(session, context);
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //如果请求头中有 Authorization 则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }

    @Override
    protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
        removeSession(session.getId());
        super.onInvalidation(session, ise, key);
    }

    @Override
    protected void onStop(Session session, SessionKey key) {
        System.out.println("[session啊]消耗啊");
        super.onStop(session, key);
    }
    @Override
    protected void onExpiration(Session session, ExpiredSessionException ese, SessionKey key) {
        logger.debug("[session啊]过期啊");
        removeSession(session.getId());
        super.onExpiration(session, ese, key);
    }

    @Override
    protected void delete(Session session) {
        logger.debug("[session啊]删除啊");
        removeSession(session.getId());
        super.delete(session);
    }

    @Override
    protected void create(Session session) {
        super.create(session);
        logger.debug("[session啊]create" + JSON.toJSONString(session));
    }

    @Override
    protected void onStop(Session session) {
        logger.info("[session啊]onStop");
        super.onStop(session);
    }

    @Override
    protected void afterStopped(Session session) {
        logger.debug("[session啊]afterStopped");
        super.afterStopped(session);
    }

    @Override
    protected void onExpiration(Session session) {
        logger.debug("[session啊]onExpiration");
        super.onExpiration(session);
    }

    @Override
    protected void afterExpired(Session session) {
        logger.debug("[session啊]afterExpired");
        super.afterExpired(session);
    }

    @Override
    protected void onChange(Session session) {
        logger.debug("[session啊]onChange");
        super.onChange(session);
    }

    @Override
    protected void validate(Session session, SessionKey key) throws InvalidSessionException {
        super.validate(session, key);
    }

    private synchronized void putSession(Session session) {
        sessionMap.put(session.getId(), session);
    }

    public static synchronized Session getSession(String sessionId) {
        return sessionMap.get(sessionId);
    }

    public  static synchronized void removeSession(Serializable sessionId) {
        sessionMap.remove(sessionId);
    }

    public static synchronized Session findSessionByUsername(String username) {
        Set<Map.Entry<Serializable, Session>> entries = sessionMap.entrySet();
        Iterator<Map.Entry<Serializable, Session>> iterator = entries.iterator();
        Session session;
        String sessionUsername;
        Map.Entry<Serializable, Session> next;

        while (iterator.hasNext()) {
            next = iterator.next();
            session = REDIS_TEMPLATE.opsForValue().get(next.getKey());
            sessionUsername = session.getAttribute("current_username") == null? null:session.getAttribute("current_username").toString();
            if (org.apache.commons.lang3.StringUtils.equals(sessionUsername, username)) {
                return session;
            }
        }
        return null;
    }

    public static synchronized Session findSessionByPhoneSign(String phoneSign) {
        Set<Map.Entry<Serializable, Session>> entries = sessionMap.entrySet();
        Iterator<Map.Entry<Serializable, Session>> iterator = entries.iterator();
        Session session;
        String sessionPhoneSign;
        Map.Entry<Serializable, Session> next;

        while (iterator.hasNext()) {
            next = iterator.next();
            session = REDIS_TEMPLATE.opsForValue().get(next.getKey());
            sessionPhoneSign = session.getAttribute("phone_sign") == null? null:session.getAttribute("phone_sign").toString();
            if (org.apache.commons.lang3.StringUtils.equals(sessionPhoneSign, phoneSign)) {
                return session;
            }
        }
        return null;
    }

    public static boolean containsKey(String key) {
        return sessionMap.containsKey(key);
    }
    public static int size() {
       return sessionMap.size();
    }
    public static int onlineUser() {
        Set<String> set = new HashSet<>();
        sessionMap.forEach((key, session) -> {
            Session redisSession = REDIS_TEMPLATE.opsForValue().get(key);
            if (!StringUtils.isEmpty(redisSession.getAttribute("current_username"))) {
                set.add(redisSession.getAttribute("current_username").toString());
            }
        });
        return set.size();
    }
}
